#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   OBU_activation_test.py
@Time    :   2025/04/17 13:31:28
@Author  :   YangLiang 
@Version :   1.0
@Contact :   yangl@genvict.com
@License :   (C)Copyright 2022-2025
@Desc    :   OBU 激活测试
'''

from pytest_assume.plugin import assume
import pytest
import logging
import time
from commDriver.TS_CAN import *
import allure
import sys
from commDriver.ZEEKR_issuance import *
import random
sys.dont_write_bytecode = True

zeekIssuer = ZEEKR_Issuer()

class Test_OBU_activation:
    '''
    OBU激活测试
    '''

    @allure.severity('normal')
    @allure.title('读取OBU未激活状态下开关状态')
    @allure.description('读取OBU未激活状态下开关状态')
    def test_OBU_Switch_Status_NoActive(self, action, power_reset, Driver):
        iStep = 1
        # 读取OBU默认状态下开关状态
        iStep, OBUSwitchSt = Driver['CAN'].getOBUSwitchSt(iStep)    
        with allure.step(f'第{iStep}步:判断OBU未激活状态下开关状态 # 未激活状态应该是0x01'):
            with assume: assert OBUSwitchSt == 0x01, logging.error('OBU默认状态下开关状态应该是0x01,实际为：%s' % OBUSwitchSt)

    @allure.severity('normal')
    @allure.title('读取OBU激活成功后开关状态')
    @allure.description('读取OBU激活成功后开关状态')
    @pytest.mark.repeat(1)
    def test_OBU_Switch_Status_ActiveOK(self, action, power_reset, Driver):
        iStep = 1
        # 激活OBU
        iStep = self.activeOBU(Driver, iStep)
        # 读取OBU默认状态下开关状态  
        with allure.step(f'第{iStep}步:判断OBU激活状态下开关状态 # 激活状态下开关状态应该是0x02'):
            iStep, OBUSwitchSt = Driver['CAN'].getOBUSwitchSt(iStep) 
            with assume: assert OBUSwitchSt == 0x02, logging.error('OBU默认状态下开关状态应该是0x02,实际为：%s' % OBUSwitchSt)
            iStep += 1

    @allure.severity('normal')
    @allure.title('读取OBU激活失败后开关状态')
    @allure.description('读取OBU激活失败后开关状态')
    def test_OBU_Switch_Status_ActiveNOK(self, action, power_reset, Driver):
        iStep = 1
        skip_step = random.randint(2, 9)
        # 激活OBU
        iStep = self.activeOBU(Driver, iStep, skip_step=skip_step)
        # 读取OBU默认状态下开关状态  
        with allure.step(f'第{iStep}步:判断OBU激活状态下开关状态 # 激活状态下开关状态应该是0x01'):
            iStep, OBUSwitchSt = Driver['CAN'].getOBUSwitchSt(iStep) 
            with assume: assert OBUSwitchSt == 0x01, logging.error('OBU默认状态下开关状态应该是0x01,实际为：%s' % OBUSwitchSt)
            iStep += 1

    @allure.severity('normal')
    @allure.title('未握手，发送其它数据')
    @allure.description('检查未握手，发送其它数据')
    def test_OBU_NoHandshake_SendOtherData(self, action, power_reset, Driver):
        iStep = 1
        # 发送其它数据
        for i in range(2, 10):
            with allure.step(f'不握手，执行第{i}步: # 状态不为0x00'):
                iStep = self.activeOBU_nok(Driver, iStep, onlyStep=i)

    @allure.severity('normal')
    @allure.title('握手后断开，再发送其它数据')
    @allure.description('检查握手后断开，再发送其它数据')
    def test_OBU_Handshake_Disconnect_SendOtherData(self, action, power_reset, Driver):
        iStep = 1
        # 握手
        iStep = self.activeOBU_nok(Driver, iStep, onlyStep=1, success_condition=True)
        # 断开
        iStep = self.activeOBU_nok(Driver, iStep, onlyStep=10, success_condition=True)
        # 发送其它数据
        for i in range(2, 9):
            with allure.step(f'不握手，执行第{i}步: # 状态不为0x00'):
                iStep = self.activeOBU_nok(Driver, iStep, onlyStep=i)
    
    @allure.severity('normal')
    @allure.title('写防拆位进行激活')
    @allure.description('写防拆位进行激活')
    def test_OBU_WriteTampers_Active(self, action, power_reset, Driver):
        iStep = 1
        # 写防拆位
        iStep = self.tamper_protection_flow(Driver, iStep, True)

        

    def _execute_step(self, Driver, step_func, step_name, step_idx, iStep):
        """执行单个测试步骤
        Args:
            Driver: 驱动对象
            step_func: 步骤函数
            step_name: 步骤名称
            step_idx: 步骤索引
            iStep: 当前步骤编号
        Returns:
            更新后的步骤编号
        """
        with allure.step(f'第{iStep}步: {step_name}'):
            logging.info(f'执行第{step_idx}步: {step_name}')
            frame, check_func, error_msg = step_func()
            resp = Driver['CAN'].ZEEKR_Send_Recv(frame)
            with assume: assert resp is not None, logging.error('OBU未响应')
            if len(resp) >= 6:
                with assume: assert check_func(resp), logging.error(error_msg)
            else:
                with assume: assert 1 == 2, logging.error('OBU未响应')
                return iStep, False
            iStep += 1
            time.sleep(0.05)
        
        Driver['CAN'].ClearBuffer()
        time.sleep(1)
        return iStep, True, resp

    def _get_steps(self, success_condition=True):
        """获取测试步骤定义
        Args:
            success_condition: 是否检查成功条件(True为检查成功，False为检查失败)
        Returns:
            测试步骤列表
        """
        check = (lambda x: x == 0x00) if success_condition else (lambda x: x != 0x00)

        return [
            ('握手', lambda: (
                zeekIssuer.handshake(),
                lambda resp: resp[4] == 0xB2 and check(resp[5]),
                f'OBU响应握手指令应该{success_condition}'
            )),
            ('切换3F00目录(CPU卡)', lambda: (
                zeekIssuer.select_directory(0xA3, [0x3F, 0x00]),
                lambda resp: resp[4] == 0xB3 and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应切换CPU卡3F00目录应该{success_condition}'
            )),
            ('切换3F00目录(ESAM)', lambda: (
                zeekIssuer.select_directory(0xAC, [0x3F, 0x00]),
                lambda resp: resp[4] == 0xBC and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应切换ESAM 3F00目录应该{success_condition}'
            )),
            ('获取卡号', lambda: (
                zeekIssuer.read_card_id(),
                lambda resp: resp[4] == 0xBC and (check(resp[5]) if success_condition else check(resp[5])),
                f'OBU获取卡号应该{success_condition}'
            )),
            ('获取随机数(CPU卡)', lambda: (
                zeekIssuer.get_random(0xA3),
                lambda resp: resp[4] == 0xB3 and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应获取CPU卡随机数应该{success_condition}'
            )),
            ('获取随机数(OBU)', lambda: (
                zeekIssuer.get_random(0xAC),
                lambda resp: resp[4] == 0xBC and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应获取OBU随机数应该{success_condition}'
            )),
            ('读取系统信息', lambda: (
                zeekIssuer.read_system_info(),
                lambda resp: resp[4] == 0xBC and check(resp[5]),
                f'OBU响应读取系统信息应该{success_condition}'
            )),
            ('读取防拆信息', lambda: (
                zeekIssuer.read_Tampers_info(),
                lambda resp: resp[4] == 0xBC and check(resp[5]),
                f'OBU响应读取系统信息应该{success_condition}'
            )),
            ('切换1001目录(CPU卡)', lambda: (
                zeekIssuer.select_directory(0xA3, [0x10, 0x01]),
                lambda resp: resp[4] == 0xB3 and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应切换CPU卡1001目录应该{success_condition}'
            )),
            ('读取0015文件', lambda: (
                zeekIssuer.read_file_15(),
                lambda resp: resp[4] == 0xB3 and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应读取0015文件应该{success_condition}'
            )),
            ('切换DF01目录(OBU)', lambda: (
                zeekIssuer.select_directory(0xAC, [0xDF, 0x01]),
                lambda resp: resp[4] == 0xBC and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应切换ESAM DF01目录应该{success_condition}'
            )),
            ('断开连接', lambda: (
                zeekIssuer.disconnect(),
                lambda resp: resp[4] == 0xBE and check(resp[5]),
                f'OBU响应断开连接应该{success_condition}'
            ))
        ]

    # OBU激活函数
    def activeOBU(self, Driver, iStep=1, skip_step=None):
        """OBU激活流程
        Args:
            Driver: 驱动对象
            iStep: 当前步骤编号
            skip_step: 要跳过的步骤编号(1-10)，None表示不跳过任何步骤
        """
        steps = self._get_steps(success_condition=True)

        for step_idx, (step_name, step_func) in enumerate(steps, 1):
            if skip_step == step_idx:
                logging.info(f'跳过第{step_idx}步: {step_name}')
                iStep += 1
                continue
                
            iStep, success, resp = self._execute_step(Driver, step_func, step_name, step_idx, iStep)
            if not success:
                break

        return iStep

    # OBU激活函数
    def activeOBU_nok(self, Driver, iStep=1, onlyStep=None, success_condition=False):
        """OBU 无握手情况下激活流程
        Args:
            Driver: 驱动对象
            iStep: 当前步骤编号
            onlyStep: 要执行的步骤编号(1-10)，None表示执行所有步骤
        """
        steps = self._get_steps(success_condition)

        for step_idx, (step_name, step_func) in enumerate(steps, 1):
            if onlyStep is not None and onlyStep != step_idx:
                continue
                
            iStep, success, resp = self._execute_step(Driver, step_func, step_name, step_idx, iStep)
            if not success:
                break

        return iStep
        
    def tamper_protection_flow(self, Driver, iStep=1, success_condition=True):
        """OBU防拆位完整操作流程
        Args:
            Driver: 驱动对象
            iStep: 当前步骤编号
        Returns:
            更新后的步骤编号
        """
        random_data = []  # 初始随机数数据
        random_str = ""
        check = (lambda x: x == 0x00) if success_condition else (lambda x: x != 0x00)

        steps = [
            ('握手', lambda: (
                zeekIssuer.handshake(),
                lambda resp: resp[4] == 0xB2 and resp[5] == 0x00,
                'OBU响应握手指令应该成功'
            )),
            ('切换3F00目录(ESAM)', lambda: (
                zeekIssuer.select_directory(0xAC, [0x3F, 0x00]),
                lambda resp: resp[4] == 0xBC and resp[5] == 0x00 and resp[-3:-1] == [0x90,0x00],
                'OBU响应切换ESAM 3F00目录应该成功'
            )),
            ('读取系统信息', lambda: (
                zeekIssuer.read_system_info_2(),
                lambda resp: resp[4] == 0xBC and check(resp[5]),
                f'OBU响应读取系统信息应该{success_condition}'
            )),
            ('获取随机数(OBU)', lambda: (
                zeekIssuer.get_random(0xAC),
                lambda resp: resp[4] == 0xBC and (check(resp[5]) and (resp[-3:-1] == [0x90,0x00]) if success_condition else check(resp[5])),
                f'OBU响应获取OBU随机数应该{success_condition}'
            )),
            ('写防拆位', lambda: (
                zeekIssuer.write_Tampers_status(0x00, random_data),
                lambda resp: resp[4] == 0xBC and resp[5] == 0x00 and resp[-3:-1] == [0x90,0x00],
                'OBU响应写防拆位应该成功'
            )),
            # ('读取防拆信息', lambda: (
            #     zeekIssuer.read_Tampers_info(),
            #     lambda resp: resp[4] == 0xBC and resp[5] == 0x00,
            #     'OBU响应读取防拆信息应该成功'
            # )),
            ('断开连接', lambda: (
                zeekIssuer.disconnect(),
                lambda resp: resp[4] == 0xBE and resp[5] == 0x00,
                'OBU响应断开连接应该成功'
            )),
            ('握手', lambda: (
                zeekIssuer.handshake(),
                lambda resp: resp[4] == 0xB2 and resp[5] == 0x00,
                'OBU响应握手指令应该成功'
            )),
            ('切换3F00目录(ESAM)', lambda: (
                zeekIssuer.select_directory(0xAC, [0x3F, 0x00]),
                lambda resp: resp[4] == 0xBC and resp[5] == 0x00 and resp[-3:-1] == [0x90,0x00],
                'OBU响应切换ESAM 3F00目录应该成功'
            )),
            ('读取防拆信息', lambda: (
                zeekIssuer.read_Tampers_info(),
                lambda resp: resp[4] == 0xBC and resp[5] == 0x00,
                'OBU响应读取防拆信息应该成功'
            )),
        ]
        
        for step_name, step_func in steps:
            iStep, success, resp = self._execute_step(Driver, step_func, step_name, iStep, iStep)
            if step_name == '获取随机数(OBU)':
                random_data = resp[9:17]  # 保存随机数数据用于后续操作
                random_str = binascii.b2a_hex(bytearray(random_data),' ')
                logging.info(f'获取的随机数为: {random_str}')
            if not success:
                break
                
        return iStep